home *** CD-ROM | disk | FTP | other *** search
/ Amiga News 95 / Amiga News 95.iso / dpat / dpat106 / safefunction / source / safefunction.s < prev    next >
Text File  |  1995-07-21  |  12KB  |  482 lines

  1. ; SafeFunction.s
  2. ;  This program patches the SetFunction() function. This tries to solve problems
  3. ;  when two programs try to patch the same function (the system may crash if
  4. ;  the programs remove their patch in the wrong order).
  5. ; This version is based on the program named SaferPatches which was placed in
  6. ; the public domain by the author:
  7. ;    Martin Adrian
  8. ;    Rullharvsgatan 3A
  9. ;    S-431 47 Mölndal
  10. ;    SWEDEN
  11. ; This version was converted to assembly language for best efficiency, and tries
  12. ; to handle better memory failures: SetFunction() is not allowed to fail, but I
  13. ; have to make some memory allocations. The problem is solved by throwing an
  14. ; "emergency block" into the free memory list on memory shortage. Moreover, all
  15. ; allocations are done with pooled allocation functions, in order to reduce
  16. ; memory fragmentation. Author:
  17. ;    Frédéric Delacroix
  18. ;    5 rue d'Artres
  19. ;    59269 Quérénaing
  20. ;    FRANCE
  21. ; This program and the source is fully public domain.
  22.  
  23. ;  version 1.0: Assembly version of Martin Adrian's program.
  24. ;  version 1.1: semaphore added to public list for easy rendez-vous.
  25. ;        No longer startable from Workbench (the best place is
  26. ;        right after SetPatch in the startup-sequence).
  27. ;  version 1.2: Enclosed critical parts in Disable()/Enable() (in case
  28. ;        an interrupt would try to execute a function whose patch
  29. ;        is not yet correctly installed). Now clears the CPU caches.
  30. ;  version 1.3: Added a low-memory handler to throw an emergency block of
  31. ;        free memory into the system pool on a memory allocation
  32. ;        failure during SetFunction(). This reduces the threat of
  33. ;        an unexpected NULL return value from SetFunction()...
  34. ;  version 1.4: All memory allocations are now done via pools, in order to
  35. ;        reduce memory fragmentation.
  36. ;  version 1.5: Some minor guide updates, wrote a french documentation.
  37.  
  38. VERSION    MACRO
  39.     dc.b    '1.5'
  40.     ENDM
  41.  
  42.     section    Startup,CODE
  43.  
  44. EMERGENCY_BLOCKSIZE    EQU    32
  45. MEMPUDDLE_SIZE    EQU    32
  46.  
  47. Start    move.l    4.w,a6
  48.     lea    Variables,a5
  49.     move.l    #20,d7    ; return code
  50.     lea    DOS.Name(pc),a1
  51.     moveq    #39,d0    ; kickstart >= 3.0 required
  52.     jsr    _LVOOpenLibrary(a6)
  53.     move.l    d0,a4
  54.     move.l    a4,d0
  55.     beq    NoDOS
  56.     move.l    #Bienvenue.MSG,d1
  57.     exg.l    a4,a6
  58.     jsr    _LVOPutStr(a6)
  59.     exg.l    a4,a6
  60.     jsr    _LVOForbid(a6)
  61.     lea    Semaphore.Name,a1
  62.     jsr    _LVOFindSemaphore(a6)
  63.     tst.l    d0
  64.     bne    AlreadyInstalled
  65.     move.l    #MEMF_PUBLIC!MEMF_CLEAR,d0
  66.     move.l    #MEMPUDDLE_SIZE,d1
  67.     move.l    d1,d2
  68.     jsr    _LVOCreatePool(a6)
  69.     move.l    a0,var_MemoryPool(a5)
  70.     beq    NoPool
  71.     suba.l    a0,a0
  72.     jsr    MakeHead
  73.     move.l    d0,var_MainHeader(a5)
  74.     beq.s    NoMemory
  75.     move.l    #_LVOSetFunction,a0
  76.     move.l    #NewSetFunction,d0
  77.     move.l    a6,a1
  78.     jsr    _LVOSetFunction(a6)
  79.     move.l    d0,var_OldSetFunc(a5)
  80.     beq.s    NoSetFunction
  81.     lea    LowMemHandler.Interrupt,a1
  82.     jsr    _LVOAddMemHandler(a6)
  83.     move.l    #EMERGENCY_BLOCKSIZE,d0
  84.     move.l    #MEMF_PUBLIC,d1
  85.     jsr    _LVOAllocMem(a6)
  86.     move.l    d0,var_EmergencyBlock(a5)
  87.     beq.s    .NoFlag
  88.     bset    #SFB_BLOCKAVAILABLE,var_Flags(a5)
  89. .NoFlag    lea    var_SetFuncSemaphore(a5),a1
  90.     move.l    #Semaphore.Name,LN_NAME(a1)
  91.     jsr    _LVOAddSemaphore(a6)
  92.     jsr    _LVOPermit(a6)
  93.     move.l    #Installed.MSG,d1
  94.     exg.l    a4,a6
  95.     jsr    _LVOPutStr(a6)
  96.     exg.l    a6,a4
  97. ; cut the seglist
  98.     lea    Start-4(pc),a0
  99.     clr.l    (a0)    ; tchac
  100.     moveq    #0,d7
  101. CloseDOS
  102.     move.l    a4,a1
  103.     jsr    _LVOCloseLibrary(a6)
  104. NoDOS    move.l    d7,d0
  105.     rts
  106.  
  107. NoSetFunction
  108.     move.l    var_MainHeader(a5),a1
  109.     move.l    #libh_SIZEOF,d0
  110.     jsr    FreeMemory
  111. NoMemory
  112.     move.l    var_MemoryPool(a5),a0
  113.     jsr    _LVODeletePool(a6)
  114. NoPool    move.l    #Failed.MSG,d1
  115. PrintAndGo
  116.     exg.l    a4,a6
  117.     jsr    _LVOPutStr(a6)
  118.     exg.l    a4,a6
  119.     bra.s    CloseDOS
  120.  
  121. AlreadyInstalled
  122.     move.l    #AlreadyInstalled.MSG,d1
  123.     moveq    #5,d7
  124.     bra.s    PrintAndGo
  125.  
  126. DOS.Name    dc.b    'dos.library',0
  127. Bienvenue.MSG    dc.b    'SafeFunction '
  128.     VERSION
  129.     dc.b    ' by F.Delacroix, public domain.',10,0
  130. Installed.MSG    dc.b    '  Patch successfully installed',10,0
  131. Failed.MSG    dc.b    '  Failed to install patch ! (not enough memory ?!)',10,0
  132. AlreadyInstalled.MSG    dc.b    '  Patch already active (you cannot remove it).',10,0
  133.  
  134.     section    ResidentPatch,CODE
  135.  
  136.     STRUCTURE    LibHeader,0
  137.      APTR    libh_LibBase    ; points to patched library
  138.      APTR    libh_Patches    ; points to list of patches
  139.      APTR    libh_NextLib    ; points to next library header
  140.     LABEL    libh_SIZEOF
  141.  
  142.     STRUCTURE    LibPatch,0
  143.      UWORD    libp_JMP    ; JMP instruction
  144.      APTR    libp_OldFunc    ; old (before patch) function
  145.      APTR    libp_NewFunc    ; new (after patch) function
  146.      WORD    libp_Offset    ; offset in jump table
  147.      APTR    libp_NextPatch    ; points to next patch in library
  148.     LABEL    libp_SIZEOF
  149.  
  150. MAGIC_OFFSET    EQU    $2357    ; prime numbers :-]
  151.  
  152.  
  153. MakeHead    ; (D0)LibHeader=MakeHead(Library)(A0)
  154.     movem.l    d1/a0-a2,-(sp)
  155.     move.l    a0,a2
  156.     move.l    #libh_SIZEOF,d0
  157.     bsr    AllocMemory
  158.     move.l    d0,a0
  159.     move.l    a0,d0
  160.     beq.s    .Fail
  161.     move.l    a2,libh_LibBase(a0)
  162. .Fail    movem.l    (sp)+,d1/a0-a2
  163.     rts
  164.  
  165. MakePatch    ; (D0)Patches=MakePatch(Offset,Next,NewFunc)(D0,A0,A1)
  166.     movem.l    d1-d2/a0-a3,-(sp)
  167.     move.l    d0,d2
  168.     move.l    a0,a2
  169.     move.l    a1,a3
  170.     move.l    #libp_SIZEOF,d0
  171.     bsr    AllocMemory
  172.     move.l    d0,a0
  173.     move.l    a0,d0
  174.     beq.s    .Fail
  175.     move.w    #$4EF9,libp_JMP(a0)    ; opcode for JMP
  176.     move.l    a3,libp_NewFunc(a0)
  177.     move.w    d2,libp_Offset(a0)
  178.     move.l    a2,libp_NextPatch(a0)
  179. .Fail    movem.l    (sp)+,d1-d2/a0-a3
  180.     rts
  181.  
  182. SF_OFFSET    EQUR    d7
  183. SF_LIBRARY    EQUR    d6
  184. SF_NEWFUNC    EQUR    d5
  185. SF_OLDFUNC    EQUR    d4
  186.  
  187. OldSetFunction    ; (D0)OldFunc=OldSetFunction(NewFunc)(A0)
  188.     move.w    SF_OFFSET,d0
  189.     ext.l    d0
  190.     exg.l    d0,a0
  191.     move.l    SF_LIBRARY,a1
  192.     move.l    var_OldSetFunc(a5),-(sp)
  193.     rts
  194.  
  195. NewSetFunction    ; (D0)OldFunc=NewSetFunction(Offset,Library,NewFunc)(A0,A1,D0)
  196.     movem.l    a0-a3/a5/d1/d4-d7,-(sp)
  197.     lea    Variables,a5
  198.     moveq    #0,SF_OLDFUNC
  199.     move.w    a0,SF_OFFSET
  200.     move.l    a1,SF_LIBRARY
  201.     move.l    d0,SF_NEWFUNC
  202.     lea    var_SetFuncSemaphore(a5),a0
  203.     jsr    _LVOObtainSemaphore(a6)
  204.     move.l    var_MainHeader(a5),a0
  205.  
  206. .NextLib
  207.     move.l    libh_NextLib(a0),d0
  208.     beq    .NoMoreLib
  209.     move.l    d0,a1
  210.     cmp.l    libh_LibBase(a1),SF_LIBRARY
  211.     beq.s    .LibFound
  212.     move.l    a1,a0
  213.     bra    .NextLib
  214. .LibFound
  215.     move.l    a0,var_HeadPtr(a5)
  216.     move.l    a1,var_HeadPtr1(a5)
  217.  
  218.     move.l    libh_Patches(a1),var_Patch(a5)
  219. .NextPatch
  220.     move.l    var_Patch(a5),a0
  221.     move.l    libp_NextPatch(a0),a1
  222.     move.l    a1,var_Patch1(a5)
  223.     beq.s    .MakeANewPatch
  224.     cmp.w    libp_Offset(a1),SF_OFFSET
  225.     ble.s    .SearchPatchOn
  226.  
  227. ; make a new patch (none installed here)
  228. .MakeANewPatch
  229.     move.w    SF_OFFSET,d0
  230.     move.l    var_Patch1(a5),a0
  231.     move.l    SF_NEWFUNC,a1
  232.     bsr    MakePatch
  233.     move.l    d0,var_Patch1(a5)
  234.     beq    .Done
  235.     move.l    SF_NEWFUNC,a0
  236.     jsr    _LVODisable(a6)
  237.     bsr    OldSetFunction
  238.     move.l    var_Patch1(a5),a0
  239.     move.l    d0,libp_OldFunc(a0)
  240.     jsr    _LVOEnable(a6)
  241.     tst.l    d0
  242.     beq.s    .OldSetFuncFailed    ; (should not happen, but let's be careful)
  243.     move.l    var_Patch(a5),a0
  244.     move.l    var_Patch1(a5),libp_NextPatch(a0)
  245.     move.l    var_Patch1(a5),SF_OLDFUNC
  246.     bra    .Done
  247. .OldSetFuncFailed
  248.     move.l    var_Patch1(a5),a1
  249.     move.l    #libp_SIZEOF,d0
  250.     bsr    FreeMemory
  251.     bra    .Done
  252.  
  253. .SearchPatchOn
  254.     move.l    var_Patch1(a5),a0
  255.     cmp.w    libp_Offset(a0),SF_OFFSET
  256.     bne    .NotThisPatch
  257.     cmp.l    a0,SF_NEWFUNC
  258.     beq.s    .RemoveLast
  259.     cmp.l    libp_OldFunc(a0),SF_NEWFUNC
  260.     bne.s    .NotRemoveLast
  261. ; user wants to remove the last patch
  262. .RemoveLast
  263.     move.l    libp_OldFunc(a0),a0
  264.     bsr    OldSetFunction    ; no need for Disable() here: let SetFunction() do it
  265.     move.l    d0,SF_OLDFUNC
  266.     move.l    var_Patch1(a5),a0
  267.     cmp.l    libp_NewFunc(a0),SF_OLDFUNC
  268.     bne.s    .BigTrouble
  269.     move.l    var_Patch1(a5),a1
  270.     move.l    var_Patch(a5),a0
  271.     move.l    libp_NextPatch(a1),libp_NextPatch(a0)
  272.     move.l    #libp_SIZEOF,d0
  273.     bsr    FreeMemory
  274.     bra.s    .LastRemoved
  275. .BigTrouble    ; if execution arrives here, than someone has changed
  276.     move.l    SF_OLDFUNC,a0    ; the vector from the outside...
  277.     move.l    a0,SF_OLDFUNC    ; we are in BIG TROUBLE, we just restore
  278.     beq    .Done        ; the original vector and pray.
  279.     bsr    OldSetFunction
  280.     moveq    #0,SF_OLDFUNC
  281.     bra    .Done
  282. .LastRemoved
  283.     move.l    var_Patch(a5),a0
  284.     cmp.w    #MAGIC_OFFSET,libp_Offset(a0)
  285.     bne    .Done
  286.     tst.l    libp_NextPatch(a0)
  287.     bne    .Done
  288. ; remove unused library header
  289.     move.l    var_HeadPtr(a5),a0
  290.     move.l    var_HeadPtr1(a5),a1
  291.     move.l    libh_NextLib(a1),libh_NextLib(a0)
  292.     move.l    #libh_SIZEOF,d0
  293.     bsr    FreeMemory
  294.     move.l    var_Patch(a5),a1
  295.     move.l    #libp_SIZEOF,d0
  296.     bsr    FreeMemory
  297.     bra    .Done
  298.  
  299. .NotRemoveLast
  300.     move.l    var_Patch1(a5),a0
  301. .RemoveLoop
  302.     move.l    libp_NextPatch(a0),var_Patch2(a5)
  303.     beq.s    .AddPatch
  304.     move.l    var_Patch2(a5),a0
  305.     cmp.w    libp_Offset(a0),SF_OFFSET
  306.     bne.s    .AddPatch
  307.     cmp.l    a0,SF_NEWFUNC
  308.     beq.s    .RemoveThisOne
  309.     cmp.l    libp_OldFunc(a0),SF_NEWFUNC
  310.     beq.s    .RemoveThisOne
  311.     move.l    a0,var_Patch1(a5)
  312.     bra.s    .RemoveLoop
  313.  
  314. .RemoveThisOne
  315.     move.l    var_Patch1(a5),a0
  316.     move.l    var_Patch2(a5),a1
  317.     move.l    libp_NextPatch(a1),libp_NextPatch(a0)
  318. ; no need for Disable() for a single instruction
  319.     move.l    libp_OldFunc(a1),libp_OldFunc(a0)
  320.     move.l    libp_NewFunc(a1),SF_OLDFUNC
  321.     move.l    #libp_SIZEOF,d0
  322.     bsr    FreeMemory
  323.     bra    .Done
  324.  
  325. .AddPatch
  326. ; let's make a new patch on this function
  327.     move.w    SF_OFFSET,d0
  328.     move.l    var_Patch(a5),a0
  329.     move.l    libp_NextPatch(a0),a0
  330.     move.l    SF_NEWFUNC,a1
  331.     bsr    MakePatch
  332.     move.l    d0,var_Patch1(a5)
  333.     beq    .Done
  334.     move.l    SF_NEWFUNC,a0
  335.     jsr    _LVODisable(a6)
  336.     bsr    OldSetFunction
  337.     move.l    var_Patch1(a5),a0
  338.     move.l    d0,libp_OldFunc(a0)
  339.     jsr    _LVOEnable(a6)
  340.     tst.l    d0
  341.     beq.s    .OldSetFuncFailed2
  342.     move.l    var_Patch(a5),a0
  343.     move.l    var_Patch1(a5),libp_NextPatch(a0)
  344.     move.l    var_Patch1(a5),SF_OLDFUNC
  345.     bra    .Done
  346. .OldSetFuncFailed2
  347.     move.l    var_Patch1(a5),a1
  348.     move.l    #libp_SIZEOF,d0
  349.     bsr    FreeMemory
  350.     bra    .Done
  351.  
  352. .NotThisPatch
  353.     move.l    var_Patch1(a5),var_Patch(a5)
  354.     bra    .NextPatch
  355.  
  356. .NoMoreLib
  357. ; create a new library header
  358.     move.l    a0,var_HeadPtr(a5)
  359.     move.l    SF_LIBRARY,a0
  360.     bsr    MakeHead
  361.     move.l    d0,var_HeadPtr1(a5)
  362.     beq.s    .Done
  363.     move.l    var_HeadPtr(a5),a0
  364.     move.l    d0,libh_NextLib(a0)
  365.     move.w    SF_OFFSET,d0
  366.     suba.l    a0,a0
  367.     move.l    SF_NEWFUNC,a1
  368.     bsr    MakePatch
  369.     move.l    d0,var_Patch(a5)
  370.     beq.s    .CantMakePatch
  371.     move.w    #MAGIC_OFFSET,d0
  372.     move.l    var_Patch(a5),a0
  373.     sub.l    a1,a1
  374.     bsr    MakePatch
  375.     move.l    var_HeadPtr1(a5),a0
  376.     move.l    d0,libh_Patches(a0)
  377.     beq.s    .NoMagic
  378.     move.l    SF_NEWFUNC,a0
  379.     jsr    _LVODisable(a6)
  380.     bsr    OldSetFunction
  381.     move.l    var_Patch(a5),a0
  382.     move.l    d0,libp_OldFunc(a0)
  383.     jsr    _LVOEnable(a6)
  384.     tst.l    d0
  385.     beq.s    .OldSetFuncFailed3
  386.     move.l    var_HeadPtr(a5),a0
  387.     move.l    var_HeadPtr1(a5),libh_NextLib(a0)
  388.     move.l    var_Patch(a5),SF_OLDFUNC
  389.     bra.s    .Done
  390. .OldSetFuncFailed3
  391.     move.l    var_HeadPtr1(a5),a1
  392.     move.l    libh_Patches(a1),a1
  393.     move.l    #libp_SIZEOF,d0
  394.     bsr.s    FreeMemory
  395. .NoMagic
  396.     move.l    var_Patch(a5),a1
  397.     move.l    #libp_SIZEOF,d0
  398.     bsr.s    FreeMemory
  399. .CantMakePatch
  400.     move.l    var_HeadPtr1(a5),a1
  401.     move.l    #libh_SIZEOF,d0
  402.     bsr.s    FreeMemory
  403. .Done    lea    var_SetFuncSemaphore(a5),a0
  404.     jsr    _LVOReleaseSemaphore(a6)
  405.     jsr    _LVOCacheClearU(a6)
  406.     move.l    SF_OLDFUNC,d0
  407.     movem.l    (sp)+,a0-a3/a5/d1/d4-d7
  408.     rts
  409.  
  410. ; flag definitions for var_Flags
  411.     BITDEF    SF,ALLOCATING,0
  412.     BITDEF    SF,BLOCKAVAILABLE,1
  413.  
  414. AllocMemory    ; (D0)Memory=AllocMemory(Size)(D0)
  415.     move.l    var_MemoryPool(a5),a0
  416.     jsr    _LVOForbid(a6)
  417.     bset    #SFB_ALLOCATING,var_Flags(a5)
  418.     jsr    _LVOAllocPooled(a6)
  419.     bclr    #SFB_ALLOCATING,var_Flags(a5)
  420.     jsr    _LVOPermit(a6)
  421.     btst    #SFB_BLOCKAVAILABLE,var_Flags(a5)
  422.     bne.s    .NoNeed
  423.     move.l    d0,-(sp)
  424.     beq.s    .Nope
  425.     move.l    #EMERGENCY_BLOCKSIZE,d0
  426.     move.l    #MEMF_PUBLIC,d1
  427.     jsr    _LVOAllocMem(a6)
  428.     move.l    d0,var_EmergencyBlock(a5)
  429.     beq.s    .Nope
  430.     bset    #SFB_BLOCKAVAILABLE,var_Flags(a5)
  431. .Nope    move.l    (sp)+,d0
  432. .NoNeed    rts
  433.  
  434. FreeMemory    ; FreeMemory(Memory,Size)(A1,D0)
  435.     move.l    var_MemoryPool(a5),a0
  436.     jmp    _LVOFreePooled(a6)
  437.  
  438. LowMemHandler.Entry
  439.     move.b    var_Flags(a1),d0
  440.     and.b    #SFF_ALLOCATING!SFF_BLOCKAVAILABLE,d0
  441.     beq.s    .DoNothing
  442.     cmp.w    #EMERGENCY_BLOCKSIZE,memh_RequestSize(a0)
  443.     bgt.s    .DoNothing
  444.     bclr    #SFB_BLOCKAVAILABLE,var_Flags(a1)
  445.     move.l    var_EmergencyBlock(a1),a1
  446.     move.l    #EMERGENCY_BLOCKSIZE,d0
  447.     jsr    _LVOFreeMem(a6)
  448.     move.l    #MEM_ALL_DONE,d0
  449.     rts
  450. .DoNothing
  451.     move.l    #MEM_DID_NOTHING,d0
  452.     rts
  453.  
  454. LowMemHandler.Interrupt
  455.     dc.l    0,0
  456.     dc.b    NT_INTERRUPT,-120
  457.     dc.l    LowMemHandler.Name
  458.     dc.l    Variables    ; IS_DATA field
  459.     dc.l    LowMemHandler.Entry
  460.  
  461. Semaphore.Name    dc.b    'SafeFunction.Semaphore',0
  462. LowMemHandler.Name    dc.b    'SafeFunction.EmergencyHandler',0
  463.  
  464.     section    Variables,BSS
  465.  
  466.     rsreset    ; these variables are protected by the semaphore
  467. var_HeadPtr    rs.l    1
  468. var_HeadPtr1    rs.l    1
  469. var_Patch    rs.l    1
  470. var_Patch1    rs.l    1
  471. var_Patch2    rs.l    1
  472. var_MainHeader    rs.l    1    ; first LibHeader
  473. var_OldSetFunc    rs.l    1    ; points to real SetFunction()
  474. var_SetFuncSemaphore    rs.b    SS_SIZE
  475. var_EmergencyBlock    rs.l    1
  476. var_MemoryPool    rs.l    1
  477. var_Flags    rs.b    1
  478. var_Flags1    rs.b    1    ; word-alignment, for now...
  479. var_SIZEOF    rs.w    0
  480.  
  481. Variables    ds.b    var_SIZEOF
  482.